package com.knowledge.share.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisTools {
    //redis连接模板信息操作
    @Autowired
    private RedisTemplate redisTemplate;

/**--------------公共方法---------------------------------**/
    /**
     * @desc 给一个指定的key设置一个缓存失效时间
     * @param key
     * @param time
     * @return
     * @param <K>
     */
    public <K> void setExpire(K key,long time){
        try {
            if(time>0){
                redisTemplate.expire(key,time, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @desc 通过key获取缓存的过期时间
     * @param key
     * @return
     */
    public <K> long getExpire(K key){
        Long expire = redisTemplate.getExpire(key, TimeUnit.SECONDS);

        if(expire !=null){
            return  expire;
        }

        return -1;
    }

    /**
     * @desc 通过key判断缓存是否存在
     * @param key
     * @return
     */
    public <K> boolean hasKey(K key){
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * @desc 根据某个key删除缓存的内容
     * @param key
     * @param <K>
     */
    public <K> void delete(K key){
        redisTemplate.delete(key);
    }

    /**
     * @desc 移除指定key的过期时间
     * @param key
     * @param <K>
     */
    public <K> void removeExpire(K key){
        redisTemplate.boundValueOps(key).persist();
    }

    /**--------------------String 类型操作 -------------------------------**/
    /**
     * @desc 根据key获取缓存的值
     * @param key
     * @return 值
     */
    public <K,V> Object get(K key){
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * @desc 把值存入到key中
     * @param key
     * @param value
     */
    public <K,V> void set(K key,V value){
        redisTemplate.opsForValue().set(key,value);
    }

    /**
     * @desc 把值存入到指定的redis中，并设置缓存时间
     * @param key
     * @param value
     * @param time
     */
    public <K,V> void set(K key,V value,long time){
        if(time>0){
            redisTemplate.opsForValue().set(key,value,time,TimeUnit.SECONDS);
        }else{
            this.set(key,value);
        }
    }

    /**
     * @desc 批量添加redis的信息内容
     * @param keyAndValue
     * @param <K>
     * @param <V>
     */
    public <K,V> void batchSet(Map<K,V> keyAndValue){
        redisTemplate.opsForValue().multiSet(keyAndValue);
    }

    /**
     * @desc 批量添加key-value 只有在健不存在时添加，
     * map中只要有一个key存在，则全部不添加
     * @param keyAndValue
     * @param <K>
     * @param <V>
     */
    public <K,V> void  batchSetIfAbsent(Map<K,V> keyAndValue){
        try {
            redisTemplate.opsForValue().multiSetIfAbsent(keyAndValue);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @desc 对一个key-value的值进行加减操作，如果key不存在则创建一个key并赋值number
     * 如果key存在，不是长整型，逻辑报错
     * @param key
     * @param number
     * @return
     * @param <K>
     */
    public <K> Long increment(K key,long number){
        return redisTemplate.opsForValue().increment(key,number);
    }

    /**
     * @desc value 进行减法操作，比如秒杀的时候我们可以设置一个值，进行数据判断。
     * @param key
     * @param number
     * @return
     * @param <K>
     */
    public <K> Long decrement(K key,long number){
        return redisTemplate.opsForValue().decrement(key,number);
    }
    /**--------------------String 类型操作 -------------------------------**/

    /**---------------------set类型 ------------------------------------**/
    /**
     * @desc 把数据存入set缓存中去
     * @param key
     * @param value
     */
    public <K,V> void sSet(K key,V... value){
        redisTemplate.opsForSet().add(key,value);
    }

    /**
     * @desc 设置过期时间的操作
     * @param key
     * @param time
     * @param value
     * @param <K>
     * @param <V>
     */
    public <K,V> void sSet(K key,long time,V... value){
        redisTemplate.opsForSet().add(key,value);
        //如果有时间设置过期时间
        if(time>0){
            setExpire(key,time);
        }
    }

    /**
     * @desc 通过key获取set对象的列表数据信息，无序随机的
     * @param key
     * @return
     */
    public <K,V> Set<V> members(K key){
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * @desc 随机获取set结构中某个元素的
     * @param key
     * @return
     * @param <K>
     */
    public <K> Object randomMember(K key){
        return redisTemplate.opsForSet().randomMember(key);
    }

    /**
     * @desc 获取集合中某个key内容的长度
     * @param key
     * @return
     * @param <K>
     */
    public <K> long sSize(K key){
        Long size = redisTemplate.opsForSet().size(key);
        if(size !=null){
            return size;
        }
        return 0;
    }

    /**
     * @desc 判断set结构中是否有某个数据是否存在 通过key、value判断
     * @param key
     * @param value
     * @return
     * @param <K>
     * @param <V>
     */
    public <K,V> boolean sHasKey(K key,V value){
        return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key,value));
    }

    /**
     * @desc 删除set结构中的某个元素，key,value删除
     * @param key
     * @param value
     * @return
     * @param <K>
     * @param <V>
     */
    public <K,V> Long sRemove(K key,V... value){
        return redisTemplate.opsForSet().remove(key,value);
    }
    /**---------------------set类型 ------------------------------------**/

    /**----------------------List 链表类型 ------------------------------------**/
    /**
     * 在变量左侧追加元素列表信息
     * @param key
     * @param value
     * @param <K>
     * @param <V>
     */
    public <K,V> void listPushLeft(K key,V value){
        redisTemplate.opsForList().leftPush(key,value);
    }

    /**
     * @desc 在右侧追加元素信息，
     * @param key
     * @param value
     * @param <K>
     * @param <V>
     */
    public <K,V> void listPushRight(K key,V value){
        redisTemplate.opsForList().rightPush(key,value);
    }

    /**
     * 把数据从左侧放入缓存中、可以直接放一个list结构
     * @param key
     * @param values
     * @param <K>
     * @param <V>
     */
    public <K,V> void listPushLeftAll(K key, List<V> values){
        redisTemplate.opsForList().leftPushAll(key,values);
    }

    /**
     * @desc 数据从右侧放入缓存中，放入一个list结构
     * @param key
     * @param values
     * @param <K>
     * @param <V>
     */
    public <K,V> void listPushRightAll(K key,List<V> values){
        redisTemplate.opsForList().rightPushAll(key,values);
    }

    /**
     * @desc 通过索引获取list中的值，
     * index >0 0表头，依次类推、index<0 -1表尾 -2 以此类推
     * @param key
     * @param index
     * @return
     * @param <K>
     * @param <V>
     */
    public <K,V> V listGetByIndex(K key,long index){
        ListOperations<K,V> listOperations = redisTemplate.opsForList();

        return listOperations.index(key,index);
    }

    /**
     * @desc 左侧弹出一条数据，并返回删除的数据
     * @param key
     * @return
     * @param <K>
     * @param <V>
     */
    public <K,V> V listPopLeft(K key){
        ListOperations<K,V> listOperations = redisTemplate.opsForList();

        return listOperations.leftPop(key);
    }

    /**
     * @desc 右侧弹出一个数据，返回删除数据
     * @param key
     * @return
     * @param <K>
     * @param <V>
     */
    public <K,V> V listPopRight(K key){
        ListOperations<K,V> listOperations = redisTemplate.opsForList();

        return listOperations.rightPop(key);
    }

    /**
     * @desc 获取list缓存数据的长度
     * @param key
     * @return
     * @param <K>
     */
    public <K> long listSize(K key){
        Long size  = redisTemplate.opsForList().size(key);

        return Objects.requireNonNullElse(size,0).longValue();
    }

    /**
     * @desc 查询list列表缓存内容的所有数据
     * @param key
     * @param start
     * @param end
     * @return
     * @param <K>
     * @param <V>
     */
    public <K,V> List<V> listRange(K key,long start,long end){
        try {
            return redisTemplate.opsForList().range(key,start,end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**----------------------List 链表类型 ------------------------------------**/
}
